require 'fastthread'
require 'thread'
require 'stringio'


module ESI
  class Response
    attr_reader :active_buffer
    attr_accessor :output

    def initialize( output )
      @lock = Mutex.new
      @count = 0
      @back_buffer = []
      @output = output
      @last_out = -1
      @threads = []
      @active_buffer = reserve_buffer 
    end

    def update_output(output)
      @lock.synchronize do
        @output = output
      end
    end

    def close_active_buffer
      @lock.synchronize do
        if !@active_buffer.closed_write?
          @active_buffer.close_write
        end
      end
    end

    def back_buffer_ready?
      @lock.synchronize do
        @back_buffer[@last_out+1..@count].size > 2
      end
    end

    def open_active_buffer
      @lock.synchronize do
        if @active_buffer.closed_write?
          begin
            @lock.unlock
            @active_buffer = reserve_buffer
          ensure
            @lock.lock
          end
        end
      end
    end

    # return's new buffer
    def reserve_buffer
      @lock.synchronize do
        buffer = @back_buffer[@count] = StringIO.new
        @count += 1
        buffer
      end
    end

    def send
      @lock.synchronize do
        # roll up requests
        until @last_out == @count
          o = @back_buffer[@last_out+1]
          if o.nil? or !o.closed_write?
            #puts "#{self} buffering...#{@last_out} to #{@count} #{@back_buffer[@last_out+1..@count].map{|b| b ? b.closed_write? : 'nil' }.join(',')}"
            break
          end
          o.rewind
          buf = o.read
          #puts "#{Thread.current} #{self} sending: #{@last_out+1} #{buf.inspect}"
          @back_buffer[@last_out+1] = nil # clear it out, release memory
          @output << buf
          @last_out += 1
        end
      end
    end

    def wait_thread(thread)
      @threads << thread
    end

    def flush
      @threads.each{|t| t.join }
      @active_buffer.close_write
      # roll up requests
      @last_out = 0 if @last_out == -1
      tail_buffer = (@back_buffer[@last_out..@back_buffer.size]||[])
      #puts "\nflushing:#{Thread.current} #{self} with #{tail_buffer.inspect}"
      while !tail_buffer.empty?
        o = tail_buffer.shift
        unless o.nil?
          o.rewind
          buf = o.read
          #puts "#{Thread.current} #{self} flush : #{buf.inspect}"
          @output << buf
        end
        #puts "#{self} sending: #{@count-tail_buffer.size}"
      end
    end

  end
end
